package com.whiteape.book.core.manager;

import android.content.Context;
import android.util.Log;


import com.bumptech.glide.Glide;
import com.whiteape.book.common.WaConstants;
import com.whiteape.book.model.bo.CategoryDetailBo;
import com.whiteape.book.model.bo.ChapterContentBo;
import com.whiteape.book.model.dto.CollectionMediaBean;
import com.whiteape.book.model.dto.DownloadTaskBean;
import com.whiteape.book.model.dto.MediaChapterBean;
import com.whiteape.book.model.dto.MediaRecordBean;
import com.whiteape.book.model.dto.SiteSourceBean;
import com.whiteape.book.model.dto.SiteSourceCategoryBean;
import com.whiteape.book.model.gen.CategoryDetailBoDao;
import com.whiteape.book.model.gen.CollectionMediaBeanDao;
import com.whiteape.book.model.gen.DaoSession;
import com.whiteape.book.model.gen.DownloadTaskBeanDao;
import com.whiteape.book.model.gen.MediaChapterBeanDao;
import com.whiteape.book.model.gen.MediaRecordBeanDao;
import com.whiteape.book.model.gen.SiteSourceBeanDao;
import com.whiteape.book.model.gen.SiteSourceCategoryBeanDao;
import com.whiteape.book.model.type.MediaType;
import com.whiteape.book.utils.BookManager;
import com.whiteape.book.utils.DaoDbHelper;
import com.whiteape.book.utils.FileUtils;
import com.whiteape.book.utils.GlideCacheUtil;
import com.whiteape.book.utils.GsonUtils;
import com.whiteape.book.utils.IOUtils;
import com.whiteape.book.utils.LogUtils;
import com.whiteape.book.utils.RxUtils;
import com.whiteape.book.utils.StringUtils;
import com.whiteape.book.utils.Void;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import io.reactivex.Scheduler;
import io.reactivex.Single;
import io.reactivex.SingleEmitter;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

/**
 * Created by newbiechen on 17-5-8.
 * 存储关于书籍内容的信息(CollBook(收藏书籍),BookChapter(书籍列表),ChapterInfo(书籍章节),BookRecord(记录))
 */

public class MediaStorageManager {
    private static final String TAG = "MediaStorageManager";
    private static volatile MediaStorageManager sInstance;
    private DaoSession mSession;
    private CollectionMediaBeanDao collectionMediaBeanDao;

    private Executor executor = Executors.newFixedThreadPool(WaConstants.FIX_THREAD_NUM);
    private Scheduler scheduler = Schedulers.from(executor);

    private MediaStorageManager() {
        mSession = DaoDbHelper.getInstance()
                .getSession();
        collectionMediaBeanDao = mSession.getCollectionMediaBeanDao();
    }

    public static MediaStorageManager getInstance() {
        if (sInstance == null) {
            synchronized (MediaStorageManager.class) {
                if (sInstance == null) {
                    sInstance = new MediaStorageManager();
                }
            }
        }
        return sInstance;
    }

    //存储已收藏书籍
    public void saveCollectionMediaWithAsync(CollectionMediaBean bean) {
        //启动异步存储
        mSession.startAsyncSession()
                .runInTx(
                        () -> {
                            bean.__setDaoSession(DaoDbHelper.getInstance().getSession());
                            if (bean.getMediaChapterList() != null) {
                                // 存储BookChapterBean
                                saveMediaChapters(bean.getMediaChapterList());
//                                mSession.getMediaChapterBeanDao()
//                                        .insertOrReplaceInTx(bean.getMediaChapterList());
                            }
                            //存储CollBook (确保先后顺序，否则出错)
                            collectionMediaBeanDao.insertOrReplace(bean);
                        }
                );
    }


    public void saveCollectionMedia(CollectionMediaBean bean) {
        bean.__setDaoSession(DaoDbHelper.getInstance().getSession());
        saveMediaChapters(bean.getMediaChapterList());
        collectionMediaBeanDao.insertOrReplace(bean);
    }

    //用替换的方式更新，其他更新书籍章节的，都是不替换
    public void updateCollectionMediaWithReplace(CollectionMediaBean bean) {
        bean.__setDaoSession(DaoDbHelper.getInstance().getSession());
        mSession.getMediaChapterBeanDao().insertOrReplaceInTx(bean.getMediaChapterList());
        collectionMediaBeanDao.insertOrReplace(bean);

    }

    public void saveSiteCategoryInRx(List<SiteSourceCategoryBean> beanList) {
        mSession.startAsyncSession().runInTx(() -> {
            mSession.getSiteSourceCategoryBeanDao().insertOrReplaceInTx(beanList);
        });
    }

    public void saveSiteCategory(List<SiteSourceCategoryBean> beanList) {
        mSession.getSiteSourceCategoryBeanDao().insertOrReplaceInTx(beanList);
    }

    public Single<List<SiteSourceCategoryBean>> getSiteCategoryBySiteHostUrl(String siteHostUrl) {
        return Single.create(emitter -> {
            try {
                emitter.onSuccess(mSession.getSiteSourceCategoryBeanDao()
                        .queryBuilder()
                        .where(SiteSourceCategoryBeanDao.Properties.SiteHostUrl.eq(siteHostUrl))
                        .list());
            } catch (Exception e) {
                emitter.onError(e);
            }
        });
    }

    //获取已经保存在数据库中的 网站分类信息
    public Single<Map<SiteSourceBean, List<SiteSourceCategoryBean>>> getSiteCategoryMap() {
        return Single.create(emitter -> {
            Map<SiteSourceBean, List<SiteSourceCategoryBean>> result = new HashMap<>();

            try {
                List<SiteSourceBean> siteSourceBeanList = getSiteSourceAll();
                Map<String, SiteSourceBean> siteSourceBeanMap = new HashMap<>();

                for (SiteSourceBean siteSourceBean : siteSourceBeanList) {
                    String siteHostUrl = siteSourceBean.getSiteHostUrl();
                    siteSourceBeanMap.put(siteHostUrl, siteSourceBean);
                }
                List<SiteSourceCategoryBean> categoryBeanList = mSession.getSiteSourceCategoryBeanDao().loadAll();

                for (SiteSourceCategoryBean categoryBean : categoryBeanList) {
                    String siteHostUrl = categoryBean.getSiteHostUrl();
                    SiteSourceBean siteSourceBean = siteSourceBeanMap.get(siteHostUrl);
                    if (siteSourceBean == null) {
                        continue;
                    }

                    List<SiteSourceCategoryBean> categoryBeanList1 = new ArrayList<>();
                    if (result.containsKey(siteSourceBean)) {
                        categoryBeanList1 = result.get(siteSourceBean);
                    }
                    categoryBeanList1 = categoryBeanList1 == null ? new ArrayList<>() : categoryBeanList1;
                    categoryBeanList1.add(categoryBean);
                    result.put(siteSourceBean, categoryBeanList1);

                }
                emitter.onSuccess(result);
            } catch (Exception e) {
                emitter.onError(e);
            }


        });
    }


    /**
     * 异步存储BookChapter
     *
     * @param beans
     */
    public void saveMediaChaptersWithAsync(List<MediaChapterBean> beans) {
        mSession.startAsyncSession()
                .runInTx(
                        () -> {
                            //存储BookChapterBean
                            saveMediaChapters(beans);
                        }
                );
    }

    public void saveMediaChapters(List<MediaChapterBean> beans) {
        //存储BookChapterBean
        List<MediaChapterBean> newBeanList = new ArrayList<>();
        for (MediaChapterBean bean : beans) {
            if (mSession.getMediaChapterBeanDao().load(bean.getId()) != null) {
                continue;
            }
            newBeanList.add(bean);
        }
        mSession.getMediaChapterBeanDao()
                .insertOrReplaceInTx(newBeanList);
        Log.d(TAG, "saveMediaChaptersWithAsync: " + "进行存储");
    }


    /**
     * 存储章节
     *
     * @param folderName
     * @param fileName
     * @param content
     */
    public void saveChapterInfo(String folderName, String fileName, String content) {
        File file = BookManager.getBookFile(folderName, fileName);
        //获取流并存储
        Writer writer = null;
        try {
            writer = new BufferedWriter(new FileWriter(file));
            writer.write(content);
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
            IOUtils.close(writer);
        }
    }

    public static void saveComicPic(String folderName, String fileName, InputStream inputStream) {

        File file = FileUtils.getFile(WaConstants.MEDIA_CACHE_PATH + folderName
                + File.separator + fileName + FileUtils.SUFFIX_WAP);
        FileUtils.saveFile(inputStream, file);
    }

    /**
     * 异步保存章节内容
     *
     * @param folderName
     * @param fileName
     * @param content
     * @return
     */
    public Disposable saveChapterInfoInRx(String folderName, String fileName, String content) {
        return Single.create(emitter -> {

            File file = BookManager.getBookFile(folderName, fileName);
            //获取流并存储
            Writer writer = null;
            try {
                writer = new BufferedWriter(new FileWriter(file));
                writer.write(content);
                writer.flush();
                emitter.onSuccess(new Void());
            } catch (IOException e) {
                e.printStackTrace();
                IOUtils.close(writer);
                emitter.onError(e);
            }
        }).subscribeOn(scheduler)
                .observeOn(scheduler)
                .subscribe();
    }

    public void saveDownloadTask(DownloadTaskBean bean) {
        mSession.getDownloadTaskBeanDao().insertOrReplace(bean);
    }

    public void saveDownloadTaskAsync(DownloadTaskBean bean) {
        mSession.startAsyncSession().runInTx(() ->
                mSession.getDownloadTaskBeanDao().insertOrReplace(bean));
    }

    public void saveMediaRecord(MediaRecordBean bean) {
        mSession.getMediaRecordBeanDao()
                .insertOrReplace(bean);
    }

    public void saveMediaRecordAsync(MediaRecordBean bean) {
        mSession.startAsyncSession().runInTx(() ->
                mSession.getMediaRecordBeanDao().insertOrReplace(bean));
    }

    /*****************************get************************************************/
    public CollectionMediaBean getShelfMedia(String bookId) {
        CollectionMediaBean bean = collectionMediaBeanDao.queryBuilder()
                .where(CollectionMediaBeanDao.Properties.Id.eq(bookId))
                .unique();
        return bean;
    }


    public List<CollectionMediaBean> getShelfBookList() {
        return collectionMediaBeanDao
                .queryBuilder()
                .orderDesc(CollectionMediaBeanDao.Properties.LastRead)
                .list();
    }


    public Single<List<CollectionMediaBean>> getMediaListInRx(int mediaType) {
        return Single.create(emitter -> {
            List<CollectionMediaBean> collectionMediaBeanList =
                    collectionMediaBeanDao
                            .queryBuilder()
                            .where(CollectionMediaBeanDao.Properties.MediaType.eq(mediaType))
                            .orderDesc(CollectionMediaBeanDao.Properties.LastRead)
                            .list();
            emitter.onSuccess(collectionMediaBeanList);
        });
    }

    //获取书籍列表
    public Single<List<MediaChapterBean>> getMediaChaptersInRx(String bookId) {
        return Single.create(new SingleOnSubscribe<List<MediaChapterBean>>() {
            @Override
            public void subscribe(SingleEmitter<List<MediaChapterBean>> e) throws Exception {
                List<MediaChapterBean> beans = mSession
                        .getMediaChapterBeanDao()
                        .queryBuilder()
                        .where(MediaChapterBeanDao.Properties.MediaId.eq(bookId))
                        .list();
                e.onSuccess(beans);
            }
        });
    }

    //获取阅读记录
    public MediaRecordBean getMediaRecord(String mediaId) {
        return mSession.getMediaRecordBeanDao()
                .queryBuilder()
                .where(MediaRecordBeanDao.Properties.MediaId.eq(mediaId))
                .unique();
    }


    public static File getBookFile(String folderName, String fileName) {
        return FileUtils.getFile(WaConstants.MEDIA_CACHE_PATH + folderName
                + File.separator + fileName + FileUtils.SUFFIX_WA);
    }

    //TODO:需要进行获取编码并转换的问题
    public List<ChapterContentBo> getComicChapterInfoBean(String folderName, String fileName) {
        File file = new File(WaConstants.MEDIA_CACHE_PATH + folderName
                + File.separator + fileName + FileUtils.SUFFIX_WA);
        if (!file.exists()) return null;
        Reader reader = null;
        String str = null;
        StringBuilder sb = new StringBuilder();
        try {
            reader = new FileReader(file);
            BufferedReader br = new BufferedReader(reader);
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.close(reader);
        }

        try {
            return GsonUtils.string2List(sb.toString(), ChapterContentBo.class);
        } catch (Exception e) {
            LogUtils.e(e.getLocalizedMessage());
            return null;
        }
    }

    /************************************************************/

    /************************************************************/
    public Disposable deleteCollMediaInRx(CollectionMediaBean bean) {
        return Single.create(e -> {
            //查看文本中是否存在删除的数据
            deleteMedia(bean.getId());
            //删除任务
            deleteDownloadTaskByMediaId(bean.getId());
            //删除目录
            deleteBookChapter(bean.getId());
            //删除CollBook
            collectionMediaBeanDao.delete(bean);
            e.onSuccess(new Void());
        }).observeOn(scheduler)
                .subscribeOn(scheduler).subscribe();
    }

    public Single<Void> deleteCollMediaListInRx(List<CollectionMediaBean> beanList) {
        return Single.create(e -> {
            //查看文本中是否存在删除的数据
            deleteMediaList(beanList);
            //删除任务
            deleteDownloadTaskListByMediaList(beanList);
            //删除目录
            deleteBookChapterList(beanList);
            //删除CollBook
            collectionMediaBeanDao.deleteInTx(beanList);
            e.onSuccess(new Void());
        });

    }


    public void deleteBookChapter(String bookId) {
        mSession.getMediaChapterBeanDao()
                .queryBuilder()
                .where(MediaChapterBeanDao.Properties.MediaId.eq(bookId))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    public void deleteBookChapterList(List<CollectionMediaBean> mediaBeanList) {
        List<String> mediaIdList = new ArrayList<>();
        for (CollectionMediaBean bean : mediaBeanList) {
            mediaIdList.add(bean.getId());
        }
        mSession.getMediaChapterBeanDao()
                .queryBuilder()
                .where(MediaChapterBeanDao.Properties.MediaId.in(mediaIdList))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    public void deleteCollBook(CollectionMediaBean bean) {
        //查看文本中是否存在删除的数据
        deleteMedia(bean.getId());
        //删除任务
        deleteDownloadTaskByMediaId(bean.getId());
        //删除目录
        deleteBookChapter(bean.getId());
        //删除CollBook
        collectionMediaBeanDao.delete(bean);
    }

    //删除书籍
    public void deleteMedia(String bookId) {
        FileUtils.deleteFile(WaConstants.MEDIA_CACHE_PATH + bookId);
    }

    //删除书籍
    public void deleteMediaList(List<CollectionMediaBean> mediaList) {
        for (CollectionMediaBean media : mediaList) {
            FileUtils.deleteFile(WaConstants.MEDIA_CACHE_PATH + media.getId());
        }
    }

    public void deleteBookRecord(String id) {
        mSession.getMediaRecordBeanDao()
                .queryBuilder()
                .where(MediaRecordBeanDao.Properties.MediaId.eq(id))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    //删除任务
    public void deleteDownloadTaskByMediaId(String mediaId) {
        mSession.getDownloadTaskBeanDao()
                .queryBuilder()
                .where(DownloadTaskBeanDao.Properties.MediaId.eq(mediaId))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    //删除任务
    public void deleteDownloadTaskListByMediaList(List<CollectionMediaBean> mediaList) {
        List<String> mediaIdList = new ArrayList<>();
        for (CollectionMediaBean bean : mediaList) {
            mediaIdList.add(bean.getId());
        }
        mSession.getDownloadTaskBeanDao()
                .queryBuilder()
                .where(DownloadTaskBeanDao.Properties.MediaId.in(mediaIdList))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    //删除任务
    public void deleteDownloadTask(DownloadTaskBean bean) {
        String taskName = bean.getTaskName();
        mSession.getDownloadTaskBeanDao()
                .queryBuilder()
                .where(DownloadTaskBeanDao.Properties.TaskName.eq(taskName))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }

    //删除任务
    //注意，理论上一个漫画对应多个下载任务，如果只是批量删除漫画里面的几个下载任务
    //应该用DownloadTaskBean的task_name来删除
    public void deleteDownloadTaskList(List<DownloadTaskBean> beanList) {
        List<String> taskNameList = new ArrayList<>();
        for (DownloadTaskBean bean : beanList) {
            taskNameList.add(bean.getTaskName());
        }
        mSession.getDownloadTaskBeanDao()
                .queryBuilder()
                .where(DownloadTaskBeanDao.Properties.TaskName.in(taskNameList))
                .buildDelete()
                .executeDeleteWithoutDetachingEntities();
    }


    public List<SiteSourceBean> getSiteSourceAll() {
        return mSession.getSiteSourceBeanDao().loadAll();
    }

    public Single<List<SiteSourceBean>> getSiteSourceAllInRxWithType(int type) {
        return Single.create(emitter -> {
            try {
                emitter.onSuccess(mSession.getSiteSourceBeanDao()
                        .queryBuilder().where(SiteSourceBeanDao.Properties.SiteType.eq(type))
                        .list());

            } catch (Exception e) {
                emitter.onError(e);
            }
        });
    }

    public long getSiteSourceCount() {
        return mSession.getSiteSourceBeanDao().count();
    }

    public SiteSourceBean getSiteSourceBeanByHostUrl(String url) {
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        return mSession.getSiteSourceBeanDao()
                .queryBuilder()
                .where(SiteSourceBeanDao.Properties.SiteHostUrl.eq(url))
                .unique();

    }

    /**
     * 获取全部有分类的网源的分类信息
     *
     * @return
     */
//    public Map<SiteSourceBean, Map<String, String>> getCategoryAll() {
//        Map<SiteSourceBean, Map<String, String>> result = new HashMap<>();
//        List<SiteSourceBean> siteSourceBeanList = getSiteSourceAll();
//        for (SiteSourceBean siteSourceBean : siteSourceBeanList) {
//            if (siteSourceBean.getSiteCategory() == null)
//                continue;
//            }
//            result.put(siteSourceBean, siteSourceBean.getMediaCategory());
//        }
//        return result;
//    }
    public List<DownloadTaskBean> getDownloadTaskAll() {
        return mSession.getDownloadTaskBeanDao().loadAll();
    }


    public DaoSession getSession() {
        return mSession;
    }

    /**
     * 根据文件名判断是否被缓存过 (因为可能数据库显示被缓存过，但是文件中却没有的情况，所以需要根据文件判断是否被缓存
     * 过)
     * <p>
     * 小说的，直接缓存的是内容，漫画的缓存的是图片url信息，只有图片url是本地文件且本地文件存在的时候，才算是cached了
     *
     * @param folderName : bookId
     * @param chapter:   chapter
     * @return
     */
    public static boolean isChapterCached(int mediaType, String folderName, MediaChapterBean chapter) {
        if (mediaType == MediaType.MEDIA_TYPE_NOVEL) {
            //小说的是在 mediaId/chapterName.wa
            File file = new File(WaConstants.MEDIA_CACHE_PATH + folderName
                    + File.separator + chapter.getName() + FileUtils.SUFFIX_WA);
            return file.exists();
        } else if (mediaType == MediaType.MEDIA_TYPE_COMIC) {
            //漫画的是在 mediaId/chapterId/n.wap
            File file = new File(WaConstants.MEDIA_CACHE_PATH + folderName
                    + File.separator + chapter.getName() + FileUtils.SUFFIX_WA);
            if (!file.exists()) {
                return false;
            }
            String content = FileUtils.getFileContent(file);
            List<ChapterContentBo> contentBoList = GsonUtils.string2List(content, ChapterContentBo.class);
            for (ChapterContentBo bo : contentBoList) {
                if (!new File(bo.getContent()).exists()) {
                    return false;
                }
            }
            return true;

        }
        return false;
    }

    public static boolean isFontCached(String name) {
        String filePath = WaConstants.FONT_CACHE_PATH + name + ".ttf";
        return new File(filePath).exists();

    }

    public void deleteDownloadedFile(List<DownloadTaskBean> downloadTaskBeanList) {
        for (DownloadTaskBean taskBean : downloadTaskBeanList) {
            String mediaId = taskBean.getMediaId();
            MediaRecordBean recordBean = getMediaRecord(mediaId);
            CollectionMediaBean collectionMediaBean = getShelfMedia(mediaId);
            collectionMediaBean.__setDaoSession(DaoDbHelper.getInstance().getSession());
            List<MediaChapterBean> chapterBeanList = collectionMediaBean.getMediaChapterList();
            if (recordBean != null) {
                int currentChapter = recordBean.getChapter();
                List<String> novelExcludeList = new ArrayList<>();
                List<String> comicExcludeList = new ArrayList<>();
                for (int i = 0; i < chapterBeanList.size(); i++) {
                    MediaChapterBean chapter = chapterBeanList.get(i);
                    if (i >= currentChapter - 1 && i <= currentChapter + 1) {
                        novelExcludeList.add(chapter.getName() + FileUtils.SUFFIX_WA);
                        comicExcludeList.add(chapter.getId());
                    }
                }
                comicExcludeList.addAll(novelExcludeList);
                //小说，漫画的，都有xxx.wa文件，小说的是具体内容，漫画的是图片索引
                //所以漫画的，要先删除具体缓存的图片
                if (collectionMediaBean.getMediaType() == MediaType.MEDIA_TYPE_NOVEL) {
                    FileUtils.deleteFile(WaConstants.MEDIA_CACHE_PATH + mediaId + File.separator, novelExcludeList);
                } else {
                    FileUtils.deleteFile(WaConstants.MEDIA_CACHE_PATH + mediaId + File.separator, comicExcludeList);
                }

            }
        }

    }

    public void saveCategoryBoListInRx(List<CategoryDetailBo> categoryDetailBoList) {
        if (categoryDetailBoList == null || categoryDetailBoList.isEmpty()) {
            return;
        }
        mSession.startAsyncSession().runInTx(() -> {
            mSession.getCategoryDetailBoDao().insertOrReplaceInTx(categoryDetailBoList);
        });

    }

    //TODO MediaType筛选
    public Single<Integer> getUsedSourceCount() {
        return Single.create(emitter -> {

            emitter.onSuccess((int) getSiteSourceCount());

        });

    }


}
